using System;
using System.Collections;
using System.Collections.Generic;
using Cinemachine;
using DG.Tweening;
using LogSystem;
using UnityEngine;
using UnityEngine.Serialization;
using XLua;
using Yoozoo.Extension;
using Yoozoo.Gameplay.Liberty.AI;

namespace Yoozoo.Gameplay.Liberty.PlayerController
{

    public enum CameraControlMode
    {
        None = 0,
        Input = 1,
        Follow = 2,
        Switch = 3, // 切换注视点
        StreetBattle = 4, // 街道战斗
    }

    public enum CameraInputMode
    {
        Player = 1,
        Car = 2,
    }

    [Serializable]
    public class CameraFollowTarget
    {
        public struct FollowData
        {
            public Vector3 position;
            public Quaternion rotation;
            public Vector3 eulerAngles;
            public Vector3 forward;
            public Vector3 velocity;
            public Vector3 predictPosition;
        }

        public Rigidbody rigidbody;
        public InputRootMotion InputRootMotion;
        public Transform targetRoot;// 跟随物体的根节点
        
        public Vector3 firstPersonOffsetStart = new Vector3(0, 1f, 0);
        public Vector3 firstPersonOffsetEnd = new Vector3(0, 1, -7.5f);
        public Vector3 firstPersonFollowAngle = new Vector3(10, 0, 0);
        public float firstPersonSkinWidth = 0.1f;
        public float firstPersonPositionFollowSpeed = 50;
        public float  firstPersonRotationFollowSpeed = 50;

        public Vector3 offsetStart = new Vector3(0, 1f, 0);
        
        public Vector3 offsetEnd = new Vector3(0, 1, -7.5f);
        
        public Vector3 followAngle = new Vector3(10, 0, 0);

        public float skinWidth = 0.1f;
        
        public float interpolationUpToSpeed = 50;
        
        public float positionFollowSpeed = 50;
        
        public float rotationFollowSpeed = 50;

        public float input2FollowRotLerpSpeed = 2;
        public Vector3 CollisionAnchorOffset = new Vector3(0,0,0);
        private Vector3 _velocity;
        public bool firstFrame;
        private Vector3 prevPosition;
        public Vector3 preDictionPosition;
        private List<FollowData> FollowDataList = new List<FollowData>(30);

        public FollowData FData
        {
            get
            {
                if (FollowDataList.Count > 0)
                {
                    return FollowDataList[FollowDataList.Count - 1];
                }

                FollowData data = new FollowData();
                data.position = rigidbody.transform.position;
                data.rotation = rigidbody.transform.rotation;
                data.eulerAngles = rigidbody.transform.eulerAngles;
                data.forward = rigidbody.transform.forward;
                AddFollowData(data);
                return data;
            }
        }
    
        public void OnFixedUpdate()
        {
            if (firstFrame)
            {
                FollowDataList.Clear();
            }
            
            FollowData data = new FollowData();
            data.position = rigidbody.transform.position;
            if (FollowDataList.Count > 0)
            {
                var lastRotation = FollowDataList[FollowDataList.Count - 1].rotation;
                var lastForward = FollowDataList[FollowDataList.Count - 1].forward;
                float angle = Vector3.Angle(lastForward, (rigidbody.transform.rotation * Vector3.forward).normalized);
                if (angle > 5)
                {
                    //data.rotation = Quaternion.Lerp(lastRotation, rigidbody.transform.rotation, 5 / angle);
                    data.rotation = rigidbody.transform.rotation;
                }
                else
                {
                    data.rotation = rigidbody.transform.rotation;
                }
            }
            else
            {
                data.rotation = rigidbody.transform.rotation;
            }

            data.eulerAngles = data.rotation.eulerAngles;
            data.forward = (data.rotation * Vector3.forward).normalized;;
            
            Vector3 v = Vector3.zero;
            // if (rigidbody.isKinematic)
            // {
            //     if (firstFrame)
            //     {
            //         firstFrame = false;
            //         v = Vector3.zero;
            //     }
            //     v = (rigidbody.transform.position - prevPosition)/Time.fixedDeltaTime;
            //     prevPosition = rigidbody.transform.position;
            //     _velocity = v;
            //     data.velocity = _velocity.magnitude * data.forward;
            //     AddFollowData(data);
            //     return;
            // }
            prevPosition = rigidbody.transform.position;
            firstFrame = false;
            _velocity = rigidbody.velocity;
            if (InputRootMotion!=null && InputRootMotion.Active &&!InputRootMotion.isOutPlayerControl)
            {
                data.predictPosition = LibertyAIUtils.PlayerController.playerEntity.AIEntity.data.TargetLerpPosition;
                AIData aiData = LibertyAIUtils.PlayerController.playerEntity.AIEntity.data;
                data.velocity = aiData.moveDirection * aiData.moveSpeed;
            }
            else
            {
                data.predictPosition = rigidbody.position;
                data.velocity = _velocity.magnitude * data.forward;
            }
            AddFollowData(data);
        }

        public void AddFollowData(FollowData d)
        {
            if (FollowDataList.Count >= 30)
            {
                FollowDataList.RemoveAt(0);
            }
            FollowDataList.Add(d);
        }
        
        public void OnDrawGizmos()
        {
#if UNITY_EDITOR
            if (FollowDataList!=null && FollowDataList.Count > 0)
            {
                Vector3 offset = Vector3.up * 0.5f;
                for (int i = FollowDataList.Count - 1; i >= 0; i--)
                {
                    FollowData data = FollowDataList[i];
                    Gizmos.color = Color.green;
                    Gizmos.DrawSphere(data.position + offset,0.15f - 0.003f * i);
                    Gizmos.color = Color.red;
                    Gizmos.DrawLine(data.position + offset,data.position + offset + data.forward * 0.2f);
                    Gizmos.color = Color.blue;
                    if (data.velocity.magnitude > 0.001f)
                    {
                        Gizmos.DrawLine(data.position + offset,data.position + offset + data.velocity.normalized * 0.2f);
                    }

                    if (i - 1 >= 0)
                    {
                        Gizmos.color = Color.yellow;
                        Gizmos.DrawLine(data.position + offset,FollowDataList[i-1].position + offset);
                    }
                }
            }
#endif
        }
        
    }

    [Serializable]
    [LuaCallCSharp]
    public class CameraInputData
    {
        public Vector3 inputAngle;

        [Space(10)]
        [Header("相机横向灵敏度")]
        public float horizontalAimingSpeed = 6f;                           
        [Header("相机纵向灵敏度")]
        public float verticalAimingSpeed = 6f;     
        
        [Space(10)]
        [Header("相机垂直方向最大角度")]
        public float maxVerticalAngle = 30f;           
        [Header("相机垂直方向最小角度")]
        public float minVerticalAngle = -60f; 
        
        
        [Space(30)]
        public float angleH = 0;
        public float angleV = 0;

        public float localAngleH;
        public float localAngleV;
    }
    
    
    [Serializable]
    public class CameraInitializedData
    {
        public Vector3 cameraAngle = new Vector3(10, 0, 0);
        
        
    }

    [Serializable]
    public class InputCameraConfig
    {
        [LuaCallCSharp]
        public CameraInputData cameraInputData;
        public CameraInitializedData initializedData;
        public CameraFollowTarget followTarget;
    }
    

    public struct GetTargetTransformParams
    {
        public CameraControlMode controlMode;
        public Vector3 followAngle;
    }
    
    

    public class PlayerInputCamera : MonoBehaviour
    {
        [SerializeField]
        private bool active;
        public bool Active
        {
            get => active;
            set => active = value;
        }

        [SerializeField]
        private CameraControlMode controlMode = CameraControlMode.Input;
        public CameraControlMode ControlMode
        {
            get => controlMode;
            set
            {
                Init();
                if (controlMode != value)
                {
                    if (isTweenAngle)
                    {
                        //Debug.LogError(LogModule.LibertyScene,"正在过渡相机视角，不允许更改相机模式！");
                        //return;
                    }
                    
                    if (value == CameraControlMode.Input || 
                        value == CameraControlMode.Switch || 
                        value == CameraControlMode.StreetBattle)
                    {
                        if (value == CameraControlMode.Input &&
                            controlMode == CameraControlMode.StreetBattle)
                        {
                            StopTweenCameraAngle();
                        }
                        
                        var cv = cameraBody.transform.eulerAngles;
                        if (cv.x > 180)
                        {
                            cv.x -= 360;
                        }
                        else if (cv.x < -180)
                        {
                            cv.x += 360;
                        }
                        //UnityEngine.Debug.LogError("cv.x: " + cv.x);
                        cameraInputData.angleV = -cv.x;
                        cameraInputData.angleH = cv.y;
                    }
                    else if (value == CameraControlMode.Follow)
                    {
                        //UnityEngine.Debug.LogError("切换跟随相机模式 => Follow");

                        ResetRecoil();
                        thirdPersonPositionSpeed = followTarget.positionFollowSpeed;
                        thirdPersonRotationSpeed = followTarget.rotationFollowSpeed;
                    }
                }

                controlMode = value;
            }
        }
        
        [SerializeField]
        private bool useFirstPerson = false;
        public bool UseFirstPerson
        {
            set => useFirstPerson = value;
            get => useFirstPerson;
        }

        [SerializeField]
        private CameraInputMode cameraInputMode = CameraInputMode.Player;

        public CameraInputMode CameraInputMode
        {
            get => cameraInputMode;
            set
            {
                cameraInputMode = value;
            }
        }
        
        [SerializeField]
        private CinemachineVirtualCamera virtualCamera;
        public CinemachineVirtualCamera VirtualCamera => virtualCamera;
        
        public PlayerInput playInput;

        public CameraInputData cameraInputData;
        public CameraInitializedData initializedData;
        public CameraFollowTarget followTarget;

        //private CameraFollowTarget.FollowData _followData;
        public CameraFollowTarget.FollowData FollowData;
        
        private Rigidbody cameraBody;

        public Vector3 CameraAngles
        {
            get
            {
                return new Vector3(cameraInputData.localAngleV,cameraInputData.localAngleH,0);
            }
        }
        
        [Tooltip("Position of the first person camera relative to the car.")]
        public Vector3 firstPersonOffsetStart = new Vector3(0, 0.5f, 0);
        [Tooltip("Position of the first person camera relative to the car.")]
        public Vector3 firstPersonOffsetEnd = new Vector3(0, 1, -3);
        [Tooltip("Rotation of the first person camera relative to the target.")]
        public Vector3 firstPersonAngle = new Vector3(10, 0, 0);
        [Tooltip("The minimum distance to keep when an obstacle is in the way of the first person camera.")]
        public float firstPersonSkinWidth = 0.1f;
        [Tooltip("Speed at which the first person camera will move to its target")]
        public float firstPersonPositionSpeed = 50;
        [Tooltip("Speed at which the first person camera will rotate to its target")]
        public float firstPersonRotationSpeed = 50;
        [Tooltip("The amount of smoothing to apply to the look offset. Can be zero.")]
        public float m_LookOffsetSmoothing = 0.05f;
        [Tooltip("Position of the target relative to the car.")]
        public Vector3 thirdPersonOffsetStart = new Vector3(0, 0.5f, 0);
        [Tooltip("Position of the camera relative to the car.")]
        public Vector3 thirdPersonOffsetEnd = new Vector3(0, 1, -3);
        [Tooltip("Rotation of the camera relative to the target.")]
        public Vector3 thirdPersonAngle = new Vector3(10, 0, 0);
        [Tooltip("The minimum distance to keep when an obstacle is in the way of the camera.")]
        public float thirdPersonSkinWidth = 0.1f;
        [Tooltip("Lowers the camera's rotation if the velocity of the rigidbody is below this value. Set to 0 to disable.")]
        public float interpolationUpToSpeed = 50;
        [Tooltip("Speed at which the camera will move to its target")]
        public float thirdPersonPositionSpeed = 50;
        [Tooltip("Speed at which the camera will rotate to its target")]
        public float thirdPersonRotationSpeed = 50;
        [Tooltip("The amount of freedom the character has on the horizontal axis before the camera starts to follow the character.")]
        public float m_HorizontalPivotFreedom = 0f;
        public float input2FollowRotLerpSpeed = 2;
        [Tooltip("The offset from the anchor position when determining if there is a collision.")]
        public Vector3 m_CollisionAnchorOffset;
        [Tooltip("The radius of the camera's collision sphere to prevent it from clipping with other objects.")]
        public float m_CollisionRadius = 0.05f;
        [Tooltip("The amount of smoothing to apply to the position when an object is obstructing the target position. Can be zero.")]
        public float m_ObstructionPositionSmoothing = 0.04f;

        [Header("后坐力")]
        // 后坐力偏移欧拉角
        [Tooltip("The euler angle offset of Horizontal and Verticle by recoil.")]
        [SerializeField]
        private Vector2 recoilAngles = Vector2.zero;    // x: Horizontal  y: Verticle
        // 后坐力范围限制
        [Tooltip("The euler angle limit of Horizontal and Verticle by recoil.")]
        [SerializeField]
        private Vector4 recoilAnglesLimit = Vector4.zero;   // xy: Horizontal  zw: Verticle
        public Vector4 RecoilAnglesLimit
        {
            set { recoilAnglesLimit = value; }
        }
        // 后坐力恢复时长
        [Tooltip("The recover duration of recoil.")]
        [SerializeField]
        private float recoilRecoverDuration = 0.5f;
        // 后坐力恢复延迟
        [Tooltip("The recover delaytime of recoil.")]
        [SerializeField]
        private float recoilRecoverDelay = 1f;
        private float recoilRecoverTimestamp = 0f;
        // 后坐力缓冲范围
        [SerializeField]
        private Vector2 recoilBufferRange = Vector2.zero;
        // 后坐力开枪时的相机旋转
        [Tooltip("The camera eulerAngle of recoil.")]
        [SerializeField]
        private Vector3 recoilCameraStartEuler = Vector3.zero;
        [SerializeField]
        private Vector3 recoilCameraEndEuler = Vector3.zero;
        private bool recoilRecordCameraEndEuler = false;

        #region smooth parameters

        public Vector3 smoothCollisionOffset;
        public float smoothColOffsetSpeed = 5;
        
        #endregion
        
        [Header("相机碰撞检测密度，越小越精确，检测次数越多")]
        [Range(0,1f)]
        public float collisionStep = 0.2f;

        [Space(10)] [Header("相机碰撞检测层级")] 
        public LayerMask playerMask;
        public LayerMask vehicleMask;

        private LayerMask colliderMask
        {
            get
            {
                if (cameraInputMode == CameraInputMode.Player)
                {
                    return playerMask;
                }
                else if (cameraInputMode == CameraInputMode.Car)
                {
                    return vehicleMask;
                }
                return playerMask;
            }
        }

        public List<Transform> ignoreList = new List<Transform>(10);

        public Vector3 cameraRotationEuler = Vector3.zero;
        
        private bool initialized = false;

        private void Awake()
        {
            if (ignoreList == null)
            {
                ignoreList = new List<Transform>(10);
            }

            m_CurrentLookOffset = thirdPersonOffsetStart;
        }

        public IEnumerator Start()
        {
            // 编辑器下使用
            yield return null;
            Init();
        }

        private void OnDrawGizmos()
        {
            if (followTarget!=null)
            {
                followTarget.OnDrawGizmos();   
            }
        }

        public void Init()
        {
            if (initialized)
            {
                return;
            }
            
            initialized = true;
            if (virtualCamera == null)
            {
                virtualCamera = GetComponent<CinemachineVirtualCamera>();
            }

            cameraBody = GetComponent<Rigidbody>();
            
            if (cameraBody == null)
            {
                cameraBody = gameObject.AddComponent<Rigidbody>();
                cameraBody.hideFlags = HideFlags.NotEditable;
            }

            cameraBody.isKinematic = false;
            cameraBody.useGravity = false;
            cameraBody.drag = 0;
            cameraBody.angularDrag = 0;
            
            m_FixedPosition = m_SmoothPosition = transform.position;
            m_FixedRotation = m_SmoothRotation = transform.rotation;
        }

        private Coroutine _switchTargetCor;
        
        public void SetCameraFollowTarget(CameraFollowTarget ft)
        {
            Active = false;

            followTarget = ft;
            followTarget.firstFrame = true;

            firstPersonOffsetStart = ft.firstPersonOffsetStart;
            firstPersonOffsetEnd = ft.firstPersonOffsetEnd;
            firstPersonAngle = ft.firstPersonFollowAngle;
            firstPersonSkinWidth = ft.firstPersonSkinWidth;
            firstPersonPositionSpeed = ft.firstPersonPositionFollowSpeed;
            firstPersonRotationSpeed = ft.firstPersonRotationFollowSpeed;
            
            thirdPersonOffsetStart = ft.offsetStart;
            thirdPersonOffsetEnd = ft.offsetEnd;
            thirdPersonAngle = ft.followAngle;
            thirdPersonSkinWidth = ft.skinWidth;
            m_CollisionAnchorOffset = ft.CollisionAnchorOffset;
            interpolationUpToSpeed = ft.interpolationUpToSpeed;
            thirdPersonPositionSpeed = ft.positionFollowSpeed;
            thirdPersonRotationSpeed = ft.rotationFollowSpeed;
            input2FollowRotLerpSpeed = ft.input2FollowRotLerpSpeed;

            smoothCollisionOffset = ft.offsetEnd;
            if (_switchTargetCor!=null)
            {
                StopCoroutine(_switchTargetCor);
                isTweenAngle = false;
            }
            ControlMode = CameraControlMode.Switch;
            _switchTargetCor = StartCoroutine(SwitchTargetUpdate());
        }

        public float targetLerpPosSpeed = 5f;
        public float targetLerpMinTime = 2f;
        public float targetLerpMaxTime = 3.5f;

        public bool isTweenAngle = false;

        IEnumerator SwitchTargetUpdate()
        {
            //isTweenAngle = true;
            playInput.CloseCameraInput = true;
            playInput.CloseMoveInput = true;
            yield return null;
            Vector3 targetEuler = FollowData.eulerAngles;
            
            float tAngleV = initializedData.cameraAngle.x - targetEuler.x;
            tAngleV = -MathUtils.NormalizeAngle(tAngleV);
            //float tAngleH = initializedData.cameraAngle.y + targetEuler.y;

            thirdPersonPositionSpeed = targetLerpPosSpeed;
            Active = true;
            
            float t = 0;            
            yield return new WaitUntil(() =>
            {
                t += Time.deltaTime;
                cameraInputData.angleV = Mathf.Lerp(cameraInputData.angleV, tAngleV, Time.deltaTime * 3);
                //cameraInputData.angleH = Mathf.LerpAngle(cameraInputData.angleH, tAngleH, Time.deltaTime * 3);
                
                //UnityEngine.Debug.LogError("AngleV: " + cameraInputData.angleV);
                return (Mathf.Abs((cameraInputData.angleV - tAngleV)) < 0.1f && t > targetLerpMinTime) || t >= targetLerpMaxTime;
            });
            //thirdPersonPositionSpeed = 5;
            playInput.CloseCameraInput = false;
            playInput.CloseMoveInput = false;
            //isTweenAngle = false;
            ControlMode = CameraControlMode.Follow;

        }

        private Coroutine _tweenCameraCor;

        public void StopTweenCameraAngle()
        {
            if (_tweenCameraCor!=null && isTweenAngle)
            {
                StopCoroutine(_tweenCameraCor);
                _tweenCameraCor = null;
                isTweenAngle = false;
            }
        }
        
        public void TweenCameraAngle(Vector3 cameraAngle,float speed,Ease easeType,Action callback = null)
        {
            ControlMode = CameraControlMode.Switch;
            _tweenCameraCor = StartCoroutine(TweenCameraAngleUpdate(cameraAngle, speed, easeType, callback));
        }

        public void TweenBattleCameraAngle(Vector3 cameraAngle, float speed, Ease easeType, Action callback = null)
        {
            _tweenCameraCor = StartCoroutine(TweenCameraAngleUpdate(cameraAngle, speed, easeType, callback));
        }
        
        IEnumerator TweenCameraAngleUpdate(Vector3 cameraAngle,float speed,Ease easeType,Action callback)
        {
            isTweenAngle = true;
            
            thirdPersonPositionSpeed = 10000;  // 不走过渡
            thirdPersonRotationSpeed = 10000;
            
            playInput.CloseCameraInput = true;
            playInput.CloseMoveInput = true;
            yield return null;
            Vector3 targetEuler = FollowData.eulerAngles;
            
            float tAngleV = cameraAngle.x - targetEuler.x;
            tAngleV = -MathUtils.NormalizeAngle(tAngleV);
            float tAngleH = cameraAngle.y + targetEuler.y;
            
            float angleV = cameraInputData.angleV;
            float angleH = cameraInputData.angleH;

            angleV = MathUtils.MinimizeEulerAngle(angleV);
            angleH = MathUtils.MinimizeEulerAngle(angleH);

            tAngleV = MathUtils.GetTargetAngle(angleV, tAngleV);
            tAngleH = MathUtils.GetTargetAngle(angleH, tAngleH);

            float offsetV = Mathf.Abs(tAngleV - angleV);
            float offsetH = Mathf.Abs(tAngleH - angleH);
            if (offsetV > 360) offsetV -= 360;
            if (offsetH > 360) offsetH -= 360;
            
            float maxOffset = Mathf.Max(offsetV, offsetH);

            float duration = maxOffset / (90f * speed);
            
            
            DOTween.To(()=> angleV, v=>
            {
                angleV = v;
                cameraInputData.angleV = angleV;
            }, tAngleV, duration).SetEase(easeType);
            
            DOTween.To(()=> angleH, h=>
            {
                angleH = h;
                cameraInputData.angleH = angleH;
            }, tAngleH, duration).SetEase(easeType);
            
            yield return new WaitForSeconds(duration);
            
            playInput.CloseCameraInput = false;
            playInput.CloseMoveInput = false;
            isTweenAngle = false;
            callback?.Invoke();
        }

        public void SetCameraInitializedData(CameraInitializedData data)
        {
            initializedData = data;
        }

        public void SetCameraInputData(CameraInputData cInputData)
        {
            cameraInputData.horizontalAimingSpeed = cInputData.horizontalAimingSpeed;
            cameraInputData.verticalAimingSpeed = cInputData.verticalAimingSpeed;
            cameraInputData.maxVerticalAngle = cInputData.maxVerticalAngle;
            cameraInputData.minVerticalAngle = cInputData.minVerticalAngle;
        }
        
        public void GetCameraInputVerticalAngleLimit(out float minVerticalAngle,out float maxVerticalAngle)
        {
            minVerticalAngle = cameraInputData.minVerticalAngle;
            maxVerticalAngle = cameraInputData.maxVerticalAngle;
        }
        
        public void ResetCamera()
        {
            Vector3 targetEuler = FollowData.eulerAngles;

            cameraInputData.angleV = initializedData.cameraAngle.x - targetEuler.x;
            cameraInputData.angleH = initializedData.cameraAngle.y + targetEuler.y;

            cameraBody.transform.eulerAngles = new Vector3(cameraInputData.angleV,cameraInputData.angleH,0);

            Quaternion camYRotation = Quaternion.Euler(0, cameraInputData.angleH, 0);
            Quaternion aimRotation = Quaternion.Euler(-cameraInputData.angleV, cameraInputData.angleH, 0);

            if (useFirstPerson)
            {
                cameraBody.transform.position = FollowData.position + camYRotation * firstPersonOffsetStart + aimRotation * smoothCollisionOffset;
            }
            else
            {
                cameraBody.transform.position = FollowData.position + camYRotation * thirdPersonOffsetStart + aimRotation * smoothCollisionOffset;
            }
                        
            // 更新插值位置旋转
            AssignFixedLocation(cameraBody.transform.rotation,cameraBody.transform.position);
            m_FixedTime = Time.time;
        }

        /// <summary>
        /// 增加后坐力
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        public void AddRecoil(float x, float y)
        {
            // 记录射击开始的相机位置
            if ((Time.time - recoilRecoverTimestamp) > recoilRecoverDuration)
            {
                recoilCameraStartEuler = transform.eulerAngles;
                recoilRecordCameraEndEuler = false;
            }
            
            recoilAngles.x = Mathf.Clamp(recoilAngles.x + x, recoilAnglesLimit.x, recoilAnglesLimit.y); // horizontal
            recoilAngles.y = Mathf.Clamp(recoilAngles.y + y, recoilAnglesLimit.z, recoilAnglesLimit.w); // verticle
            // 更新后坐力恢复时间戳
            recoilRecoverTimestamp = Time.time + recoilRecoverDelay;
        }

        /// <summary>
        /// 重置后坐力
        /// </summary>
        public void ResetRecoil()
        {
            recoilAngles = Vector2.zero;
            recoilRecoverTimestamp = 0;
            recoilRecordCameraEndEuler = false;
        }

        /// <summary>
        /// 刷新后坐力
        /// </summary>
        /// <param name="deltaTime"></param>
        void UpdateRecoil()
        {
            if ((recoilAngles.x != 0 || recoilAngles.y != 0) && Time.time >= recoilRecoverTimestamp)
            {
                // 记录射击结束的相机位置
                if (!recoilRecordCameraEndEuler)
                {
                    recoilCameraEndEuler = transform.eulerAngles;
                    recoilRecordCameraEndEuler = true;
                    
                    // 射击初始位置
                    float x_start = Mathf.DeltaAngle(0, recoilCameraStartEuler.x);
                    float y_start = recoilCameraStartEuler.y;
                    // 射击结束位置
                    float x_end = Mathf.DeltaAngle(0, recoilCameraEndEuler.x);
                    float y_end = recoilCameraEndEuler.y;
                    // 手动改变量
                    float x_hand = cameraInputData.angleH - recoilCameraStartEuler.y;
                    float y_hand = Mathf.DeltaAngle(0, -cameraInputData.angleV) - Mathf.DeltaAngle(0, recoilCameraStartEuler.x);
                    
                    // 水平方向是否需要恢复后坐力
                    if (recoilAngles.x != 0)
                    {
                        // 镜头水平旋转角度较大，停留在当前位置
                        if (-recoilBufferRange.x > x_hand || x_hand > recoilBufferRange.x)
                        {
                            cameraInputData.angleH += recoilAngles.x;
                            recoilAngles.x = 0;
                        }
                    }

                    // 垂直方向是否需要恢复后坐力
                    if (recoilAngles.y != 0)
                    {
                        if (y_hand > 0)
                        {
                            if (x_end < x_start)
                            {
                                // 没压住抢
                                recoilAngles.y = - (x_end - x_start);
                                cameraInputData.angleV = -x_end - recoilAngles.y;
                            }
                            else
                            {
                                if (Mathf.Abs(x_end - x_start) > recoilBufferRange.y)
                                {
                                    // 镜头向下旋转角度较大，停留在当前位置
                                    recoilAngles.y = 0;
                                    cameraInputData.angleV = -x_end;
                                }
                                else
                                {
                                    recoilAngles.y = - (x_end - x_start);
                                    cameraInputData.angleV = -x_end - recoilAngles.y;
                                }
                            }

                        }
                        else
                        {
                            if (y_hand < -recoilBufferRange.y)
                            {
                                // 镜头向上旋转角度较大，停留在当前位置
                                recoilAngles.y = - y_hand;
                                cameraInputData.angleV = -x_end - recoilAngles.y;
                            }

                        }
                    }
                }

                float deltaTime = Time.time - recoilRecoverTimestamp;
                float percent = Mathf.Clamp01(deltaTime / recoilRecoverDuration);
                recoilAngles.x = Mathf.Lerp(recoilAngles.x, 0, percent);
                recoilAngles.y = Mathf.Lerp(recoilAngles.y, 0, percent);
            }
        }

        // 返回(-180, 180]
        float formatVerticalAngle(float angle)
        {
            while (angle > 180)
            {
                angle -= 360;
            }

            while (angle <= -180)
            {
                angle += 360;
            }

            return angle;
        }

        private bool m_FixedUpdate = false;

        void FixedUpdate()
        {
            if (!initialized) return;
            if (!Active)
            {
                cameraBody.velocity = Vector3.zero;
                cameraBody.angularVelocity = Vector3.zero;
                return;
            }
            
            if (followTarget == null || followTarget.rigidbody == null) return;
            if (ControlMode == CameraControlMode.None) return;
            if (LibertyAIUtils.PlayerController.playerEntity.AIEntity.data.hp <= 0)
            {
                return;
            }
            RestoreFixedLocation();
            // if (!m_FixedUpdate) {
            // }else{
            //     Debug.LogError("m_FixedUpdate is false:    " + transform.position);
            // }
            followTarget.OnFixedUpdate();
            FollowData = followTarget.FData;
            // UnityEngine.Debug.LogError("预测运动未来的位置：" + FollowData.predictPosition);
            if (ControlMode == CameraControlMode.Input)
            {
                thirdPersonPositionSpeed = 10000;  // 不走过渡
                thirdPersonRotationSpeed = 10000;
            }
            else if(ControlMode == CameraControlMode.Follow)
            {
                thirdPersonPositionSpeed = followTarget.positionFollowSpeed;
                thirdPersonRotationSpeed = followTarget.rotationFollowSpeed;
            }
            else if (ControlMode == CameraControlMode.StreetBattle)
            {
                thirdPersonPositionSpeed = 10000;  // 不走过渡
                thirdPersonRotationSpeed = 10000;
            }
            
            cameraBody.interpolation = followTarget.rigidbody.interpolation;
            Vector3 previousPosition = transform.position;
            Quaternion previousRotation = transform.rotation;

            Vector3 targetPosition = Vector3.zero;
            Quaternion targetRotation = Quaternion.identity;
            
            if (controlMode != CameraControlMode.Follow)
            {
                float xValue = playInput.CameraHorValue;
                float yValue = playInput.CameraVerValue;
                // 归一化，不同分辨率的屏幕拖动单屏旋转角度一致
                xValue = (xValue / Screen.width) * 1334f; 
                yValue = (yValue / Screen.height) * 750f;
                xValue *= 0.05f * cameraInputData.horizontalAimingSpeed;
                yValue *= 0.05f * cameraInputData.verticalAimingSpeed;
                cameraInputData.angleH += xValue;
                cameraInputData.angleV += yValue;
                cameraInputData.angleV = 
                    Mathf.Clamp(cameraInputData.angleV, cameraInputData.minVerticalAngle, cameraInputData.maxVerticalAngle);
                Quaternion aimRotation = Quaternion.Euler(-cameraInputData.angleV, cameraInputData.angleH, 0);
                cameraInputData.angleH = aimRotation.eulerAngles.y;
                aimRotation = Quaternion.Euler(aimRotation.eulerAngles.x-recoilAngles.y, aimRotation.eulerAngles.y+recoilAngles.x, 0);
                targetRotation = aimRotation;
            }
            else
            {
                float rx = cameraBody.transform.rotation.eulerAngles.x;
                rx = MathUtils.NormalizeAngle(rx);
                rx = Mathf.LerpAngle(rx,thirdPersonAngle.x,Time.fixedDeltaTime * input2FollowRotLerpSpeed);

                Vector3 tAngle = thirdPersonAngle;
                tAngle.x = rx;
                
                GetTargetTransformParams transformParams = new GetTargetTransformParams()
                {
                    followAngle = tAngle,//thirdPersonAngle,
                    controlMode = ControlMode,
                };
                getTargetTransforms(transformParams,targetPosition, out targetRotation);
            }
            transform.rotation = targetRotation;
            if (useFirstPerson)
            {
                smoothCollisionOffset = Vector3.Lerp(smoothCollisionOffset, firstPersonOffsetEnd, Time.fixedDeltaTime * smoothColOffsetSpeed);
            }
            else
            {
                smoothCollisionOffset = Vector3.Lerp(smoothCollisionOffset, thirdPersonOffsetEnd, Time.fixedDeltaTime * smoothColOffsetSpeed);
            }
            targetPosition = CalCameraMovePosition();
            AssignFixedLocation(targetRotation,targetPosition);
            m_FixedTime = Time.time;
            //后坐力恢复
            UpdateRecoil();
        }
        private Vector3 m_CurrentLookOffset;
        private Vector3 m_SmoothLookOffsetVelocity;
        private RaycastHit m_RaycastHit;
        private Vector3 m_ObstructionSmoothPositionVelocity;
        private Vector3 CalCameraMovePosition()
        {
            var characterRotation = FollowData.rotation;
            var characterPosition = FollowData.predictPosition;
            // Prevent obstruction from other objects. Check for obstruction against character player position rather than the look position because the character should always be visible. It doesn't
            // matter as much if the look position isn't directly visible.
            
            var anchorPosition = characterPosition;//这个值可以跟随角色的位置，也可以是其他的位置
            var localAnchorPosition = InverseTransformPoint(anchorPosition, Quaternion.LookRotation(transform.forward, characterRotation * Vector3.up), transform.position);
            m_CurrentLookOffset = Vector3.SmoothDamp(m_CurrentLookOffset, smoothCollisionOffset, ref m_SmoothLookOffsetVelocity, m_LookOffsetSmoothing);
            var lookPosition = anchorPosition + (m_CurrentLookOffset.y *  (characterRotation * Vector3.up)) + ((m_CurrentLookOffset.z) * transform.forward);
            
            lookPosition += (m_CurrentLookOffset.x + m_HorizontalPivotFreedom * Mathf.Sign(localAnchorPosition.x)) * transform.right;
            
            // Smoothly move into position.
            var targetPosition = lookPosition;
            
            var direction = lookPosition - (anchorPosition + m_CollisionAnchorOffset);
            var normalizedDirection = direction.normalized;
            var directionMagnitude = direction.magnitude;
            // Fire a sphere to prevent the camera from colliding with other objects.
            if (Physics.SphereCast(anchorPosition + m_CollisionAnchorOffset - normalizedDirection * m_CollisionRadius, m_CollisionRadius, normalizedDirection, out m_RaycastHit, directionMagnitude,
                    colliderMask, QueryTriggerInteraction.Ignore)) {
                // Move the camera in if the character isn't in view.
                targetPosition = m_RaycastHit.point + m_RaycastHit.normal * m_CollisionRadius;

                targetPosition = Vector3.SmoothDamp(transform.position,
                    targetPosition, ref m_ObstructionSmoothPositionVelocity, m_ObstructionPositionSmoothing);

                // Keep a constant height if there is nothing getting in the way of that position.
                var localDirection = TransformDirection(direction, characterRotation);
                if (localDirection.y > 0) {
                    // Account for local y values.
                    var constantHeightPosition = InverseTransformPoint(characterPosition, characterRotation, targetPosition);
                    constantHeightPosition.y = InverseTransformPoint(characterPosition, characterRotation, lookPosition).y;
                    constantHeightPosition = TransformPoint(characterPosition, characterRotation, constantHeightPosition);
                    if (!Physics.SphereCast(anchorPosition + m_CollisionAnchorOffset - normalizedDirection * m_CollisionRadius, m_CollisionRadius, normalizedDirection,
                            out m_RaycastHit, directionMagnitude - m_CollisionRadius, colliderMask, QueryTriggerInteraction.Ignore)) {
                        targetPosition = constantHeightPosition;
                    }
                }
            }
            
            // Prevent the camera from clipping with the character.
            // Collider containsCollider;
            // if ((containsCollider = m_CharacterLocomotion.BoundsCountains(targetPosition)) != null) {
            //     targetPosition = containsCollider.ClosestPointOnBounds(targetPosition);
            // }
            
            // The target position should never be lower than the character's position. This may happen if the camera is trying to be positioned below water.
            var localTargetPosition = InverseTransformPoint(characterPosition, characterRotation, targetPosition);
            if (localTargetPosition.y < 0) {
                localTargetPosition.y = 0;
                targetPosition = TransformPoint(characterPosition, characterRotation, localTargetPosition);
            }

            return targetPosition;
        }
        /// <summary>
        /// Transforms the direction from local space to world space. This is similar to Transform.TransformDirection but does not require a Transform.
        /// </summary>
        /// <param name="direction">The direction to transform from local space to world space.</param>
        /// <param name="rotation">The world rotation of the object.</param>
        /// <returns>The world space direction.</returns>
        private Vector3 TransformDirection(Vector3 direction, Quaternion rotation)
        {
            return rotation * direction;
        }
        /// <summary>
        /// Transforms the position from world space to local space. This is similar to Transform.InverseTransformPoint but does not require a Transform.
        /// </summary>
        /// <param name="worldPosition">The world position of the object.</param>
        /// <param name="rotation">The world rotation of the object.</param>
        /// <param name="position">The position of the object.</param>
        /// <returns>The local space position.</returns>
        private Vector3 InverseTransformPoint(Vector3 worldPosition, Quaternion rotation, Vector3 position)
        {
            var diff = position - worldPosition;
            return Quaternion.Inverse(rotation) * diff;
        } 
        /// <summary>
        /// Transforms the position from local space to world space. This is similar to Transform.TransformPoint but does not require a Transform.
        /// </summary>
        /// <param name="worldPosition">The world position of the object.</param>
        /// <param name="rotation">The world rotation of the object.</param>
        /// <param name="localPosition">The local position of the object.</param>
        /// <returns>The world space position.</returns>
        public Vector3 TransformPoint(Vector3 worldPosition, Quaternion rotation, Vector3 localPosition)
        {
            return worldPosition + (rotation * localPosition);
        }
        private void AssignFixedLocation(Quaternion targetRotation, Vector3 targetPosition)
        {
            m_FixedPosition = targetPosition;
            m_FixedRotation = targetRotation;
            m_FixedUpdate = true;
            // Debug.LogError("AssignFixedLocation:" + m_FixedPosition);
        }

        protected Vector3 m_FixedPosition;
        protected Quaternion m_FixedRotation;
        protected Vector3 m_SmoothPosition;
        protected Quaternion m_SmoothRotation;
        private float m_FixedTime;
        private void RestoreFixedLocation()
        {
            m_SmoothPosition = m_FixedPosition;
            m_SmoothRotation = m_FixedRotation;
            transform.SetPositionAndRotation(m_SmoothPosition, m_SmoothRotation);
            // Debug.LogError("RestoreFixedLocation:" + transform.position);
        }

        private Vector3 currentPos;
        private Vector3 prevPos;
        private Vector3 direction;
        private void Update()
        {
            var interpAmount = (Time.time - m_FixedTime) / Time.fixedDeltaTime;
            if (float.IsNaN(interpAmount)) {
                interpAmount = 0;
            }
            m_FixedUpdate = false;
            transform.SetPositionAndRotation(Vector3.Lerp(m_SmoothPosition, m_FixedPosition, interpAmount), Quaternion.Slerp(m_SmoothRotation, m_FixedRotation, interpAmount));
            // Debug.LogError("Update Posisition:" + transform.position + "      interpAmount:" + interpAmount);
        }

        private void getTargetTransforms(GetTargetTransformParams transformParams, 
            Vector3 targetPosition, out Quaternion targetRotation,
            bool ignoreSpeed = false)
        {
            Quaternion followRotation = FollowData.rotation;
            targetRotation = transform.rotation;
            float interpolationMultiplier = 1;
            Rigidbody body = followTarget.rigidbody;
            if (body != null)
            {
                float forwardVelocity = Vector3.Dot(FollowData.velocity, FollowData.forward);
                interpolationMultiplier = interpolationUpToSpeed > 0 && !ignoreSpeed ? Mathf.Clamp01(forwardVelocity / interpolationUpToSpeed) : 1f;
                
                Vector3 rotationDirection = Vector3.up;
                if (cameraInputMode == CameraInputMode.Player)
                {
                    rotationDirection = Vector3.LerpUnclamped(FollowData.forward, FollowData.velocity, Mathf.Clamp01(forwardVelocity));
                }
                else// if (CameraInputMode == CameraInputMode.Car)
                {
                    rotationDirection = FollowData.forward;
                }
                
                followRotation = Quaternion.LookRotation(rotationDirection, Vector3.back);

                if (CameraInputMode == CameraInputMode.Player)
                {
                    if (playInput.VerRealValue < 0)
                    {
                        //interpolationMultiplier = 0.02f;
                        interpolationMultiplier = interpolationUpToSpeed > 0 && !ignoreSpeed
                            ? Mathf.Clamp01(Mathf.Abs(forwardVelocity) / interpolationUpToSpeed)
                            : 1f;
                        interpolationMultiplier *= 0.334f;
                        float v = 1 - Mathf.Abs(playInput.VerRealValue) /
                            (Mathf.Abs(playInput.HorRealValue) + Mathf.Abs(playInput.VerRealValue));
                        if (v < 0.01)
                        {
                            rotationDirection = -FollowData.velocity.normalized;
                            followRotation = Quaternion.LookRotation(rotationDirection, Vector3.back);
                        }
                        else
                        {
                            rotationDirection = FollowData.velocity.normalized;
                            followRotation = Quaternion.LookRotation(rotationDirection, Vector3.back);
                        }
                    }
                }

            }

            Vector3 rotationEuler = Vector3.zero;
            Quaternion xzRotation = Quaternion.identity;

            if (transformParams.controlMode == CameraControlMode.Follow)
            {
                rotationEuler = transformParams.followAngle + Vector3.up * followRotation.eulerAngles.y;
                xzRotation = Quaternion.Euler(new Vector3(rotationEuler.x, targetRotation.eulerAngles.y, rotationEuler.z));
            }
            else
            {
                return;
            }
            cameraRotationEuler = rotationEuler;
            Quaternion lerpedRotation = Quaternion.Euler(rotationEuler);
            targetRotation = Quaternion.Lerp(xzRotation, lerpedRotation, interpolationMultiplier);
        }
        
        private static Quaternion getShortestRotation(Quaternion a, Quaternion b)
        {
            if (Quaternion.Dot(a, b) < 0)
            {
                return a * Quaternion.Inverse(multiplyQuaternion(b, -1));
            }
            else return a * Quaternion.Inverse(b);
        }

        private static Quaternion multiplyQuaternion(Quaternion input, float scalar)
        {
            return new Quaternion(input.x * scalar, input.y * scalar, input.z * scalar, input.w * scalar);
        }
        
        private bool isChildOf(Transform source, Transform target)
        {
            Transform child = source;
            while (child != null)
            {
                if (child == target) return true;
                child = child.parent;
            }
            return false;
        }

        private bool isIgnore(Transform source)
        {
            foreach (var ig in ignoreList)
            {
                if (isChildOf(source,ig))
                {
                    return true;
                }
            }
            return false;
        }
        
        #region 视野检测

        
        // Double check for collisions: concave objects doesn't detect hit from outside, so cast in both directions.
        bool DoubleViewingPosCheck(Vector3 checkPos)
        {
            return ViewingPosCheck (checkPos) && ReverseViewingPosCheck (checkPos);
        }

        // Check for collision from camera to player.
        bool ViewingPosCheck(Vector3 checkPos)
        {
            // Cast target and direction.
            Vector3 target = FollowData.position + thirdPersonOffsetStart;
            Vector3 direction = target - checkPos;
            // If a raycast from the check position to the player hits something...
            if (Physics.SphereCast(checkPos, collisionStep, direction, out RaycastHit hit, direction.magnitude,colliderMask))
            {
                // ... if it is not the player...
                if (!isChildOf(hit.transform,followTarget.targetRoot) && !isIgnore(hit.transform) && 
                    !hit.collider.isTrigger)
                {
                    // This position isn't appropriate.
                    return false;
                }
            }
            // If we haven't hit anything or we've hit the player, this is an appropriate position.
            return true;
        }

        // Check for collision from player to camera.
        bool ReverseViewingPosCheck(Vector3 checkPos)
        {
            // Cast origin and direction.
            Vector3 origin = FollowData.position + thirdPersonOffsetStart;
            Vector3 direction = checkPos - origin;
            if (Physics.SphereCast(origin, collisionStep, direction, out RaycastHit hit, direction.magnitude,colliderMask))
            {
                if (!isChildOf(hit.transform,followTarget.targetRoot) && !isIgnore(hit.transform) && 
                    !hit.collider.isTrigger)
                {
                    return false;
                }
            }
            return true;
        }
        

        #endregion
        
    }
    
}